/**
 * Clase abstracta de la que heredan las diferentes estados del juego
 *
 * @file State.h
 * @author Adrián Guerra Marrero
 * @date 20110421 - Initial Release
 */
 
#ifndef   STATE_H
#define   STATE_H

#include <cassert>
#include <SFML/System.hpp>
#include "SGE.h"

namespace SGE
{
class State
{
public:
	/**
	 * State destructor
	 */
	virtual ~State()
	{
		if(NULL != mApp)
		{
			// Output to log file
			mApp->mLog << "State::~State() dtor with ID=" << mID << " was called" << std::endl;
		}

		// Clear out pointers that we don't need anymore
		mApp = NULL;
	}

	/**
	 * GetID will return the ID used to identify this State object
	 * @return SGE::typeStateID is the ID for this State object
	 */
	const SGE::typeStateID GetID(void) const
	{
		return mID;
	}

	/**
	 * DoInit is responsible for initializing this State.  HandleCleanup will
	 * be called if mCleanup is true so Derived classes should always call
	 * State::DoInit() first before initializing their assets.
	 */
	virtual void DoInit(void)
	{
		// Output to log file
		mApp->mLog << "State::DoInit() with ID=" << mID << " was called" << std::endl;
		// If Cleanup hasn't been called yet, call it now!
		if(true == mCleanup)
		{
			HandleCleanup();
		}
		// Initialize if necessary
		if(false == mInit)
		{
			mInit = true;
			mPaused = false;
			mElapsedTime = 0.0f;
			mElapsedClock.Reset();
			mPausedTime = 0.0f;
			mPausedClock.Reset();
		}
	}

	/**
	 * ReInit is responsible for Reseting this state when the
	 * StateManager::ResetActiveState() method is called.  This way a Game
	 * State can be restarted without unloading and reloading the game assets
	 */
	virtual void ReInit(void) = 0;

	/**
	 * DeInit is responsible for marking this state to be cleaned up
	 */
	void DeInit(void)
	{
		mApp->mLog << "State::DeInit() with ID=" << mID << " was called" << std::endl;
		if(true == mInit)
		{
			mCleanup = true;
			mInit = false;
			mElapsedTime += mElapsedClock.GetElapsedTime();
			if(true == mPaused)
			{
				mPausedTime += mPausedClock.GetElapsedTime();
			}
		}
	}

	/**
	 * IsInitComplete will return true if the DoInit method has been called
	 * for this state.
	 * @return true if DoInit has been called, false otherwise or if DeInit
	 *         was called
	 */
	bool IsInitComplete(void)
	{
		return mInit;
	}

	/**
	 * IsPaused will return true if this state is not the currently active
	 * state.
	 * @return true if state is not current active state, false otherwise
	 */
	bool IsPaused(void)
	{
		return mPaused;
	}

	/**
	 * Pause is responsible for pausing this State since the Application
	 * may have lost focus or another State has become activate.
	 */
	virtual void Pause(void)
	{
		if(NULL != mApp)
		{
			// Output to log file
			mApp->mLog << "State::Pause() with ID=" << mID << " was paused" << std::endl;
		}

		if(false == mPaused)
		{
			mPaused = true;
			mPausedClock.Reset();
		}
	}

	/**
	 * Resume is responsible for resuming this State since the Application
	 * may have gained focus or the previous State was removed.
	 */
	virtual void Resume(void)
	{
		if(NULL != mApp)
		{
			// Output to log file
			mApp->mLog << "State::Resume() with ID=" << mID << " was resumed" << std::endl;
		}

		if(true == mPaused)
		{
			mPaused = false;
			mPausedTime += mPausedClock.GetElapsedTime();
		}
	}

	/**
	 * HandleEvents is responsible for handling input events for this
	 * State when it is the active State.
	 * @param[in] theEvent to process from the App class Loop method
	 */
	virtual void HandleEvents(sf::Event theEvent) = 0;

	/**
	 * Update is responsible for handling all State update needs for this
	 * State when it is the active State.
	 */
	virtual void Update(void) = 0;

	/**
	 * Draw is responsible for handling all Drawing needs for this State
	 * when it is the Active State.
	 */
	virtual void Draw(void) = 0;

	/**
	 * HandleCleanup is responsible for calling Cleanup if this class has been
	 * flagged to be cleaned up after it completes the game loop.
	 */
	void HandleCleanup(void)
	{
		if(true == mCleanup)
		{
			// Call cleanup
			Cleanup();

			// Clear our cleanup flag
			mCleanup = false;
		}
	}

	/**
	 * GetElapsedTime will return one of the following:
	 * 1) If this state is not paused: total elapsed time since DoInit was called
	 * 2) If this state is paused: total elapsed time since Pause was called
	 * 3) If this state is not initialized: total elapsed time from DoInit to DeInit
	 * @return total elapsed time as described above.
	 */
	float GetElapsedTime(void) const
	{
		float result = mElapsedClock.GetElapsedTime();

		if(false == mInit)
		{
			result = mElapsedTime;
		}

		return result;
	}

protected:
	/// Pointer to the App class
	App* mApp;

	/**
	 * State constructor is private because we do not allow copies of our
	 * Singleton class
	 * @param[in] theID to use for this State object
	 * @param[in] theApp is a pointer to the App derived class
	 */
	State(const typeStateID theID, App* theApp) :
		mApp(theApp),
		mID(theID),
		mInit(false),
		mPaused(false),
		mCleanup(false),
		mElapsedTime(0.0f),
		mPausedTime(0.0f)
	{
		// Check that our pointer is good
		assert(NULL != theApp && "State::State() theApp pointer is bad");

		// Keep a copy of our Application pointer
		mApp = theApp;

		// Output to log file
		if(NULL != mApp)
		{
			mApp->mLog << "State::State() ctor with ID=" << mID << " was called" << std::endl;
		}
	}

	/**
	 * Cleanup is responsible for performing any cleanup required before
	 * this State is removed.
	 */
	virtual void Cleanup(void)
	{
		// Output to log file
		mApp->mLog << "State::Cleanup() with ID=" << mID << " was called" << std::endl;
	}

private:
	/// The State ID
	const typeStateID mID;
	/// Boolean that indicates that DoInit has been called
	bool mInit;
	/// State is currently paused (not active)
	bool mPaused;
	/// State needs to be cleaned up at the end of the next game loop
	bool mCleanup;
	/// Clock will help us keep track of running and paused elapsed time
	sf::Clock mElapsedClock;
	/// Total elapsed time since DoInit was called
	float mElapsedTime;
	/// Clock will help us keep track of time paused
	sf::Clock mPausedClock;
	/// Total elapsed time paused since DoInit was called
	float mPausedTime;

	/**
	 * Our copy constructor is private because we do not allow copies of
	 * our Singleton class
	 */
	State(const State&);  // Intentionally undefined

	/**
	 * Our assignment operator is private because we do not allow copies
	 * of our Singleton class
	 */
	State& operator=(const State&); // Intentionally undefined

}; // Class State
} // Namespace SGE

#endif // STATE_H
